{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 题目\n",
    "设计一个控制台程序，可以给人搭配嘻哈风格（T恤，垮裤，运动鞋）或白领风格（西装，领带，皮鞋）的衣服并展示，类似QQ秀那样的。\n",
    "\n",
    "## 基础版本"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person():\n",
    "    \n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "    \n",
    "    def wear_T_shirts(self):\n",
    "        print(\"T恤\")\n",
    "    \n",
    "    def wear_big_trouser(self):\n",
    "        print(\"垮裤\")\n",
    "        \n",
    "    def wear_sneakers(self):\n",
    "        print(\"运动鞋\")\n",
    "    \n",
    "    def wear_suit(self):\n",
    "        print(\"西装\")\n",
    "        \n",
    "    def wear_tie(self):\n",
    "        print(\"领带\")\n",
    "        \n",
    "    def wear_leather_shoes(self):\n",
    "        print(\"皮鞋\")\n",
    "    \n",
    "    def show(self):\n",
    "        print(\"装扮的{}\".format(self.name))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "客户端代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第一种装扮\n",
      "T恤\n",
      "垮裤\n",
      "运动鞋\n",
      "装扮的张贺\n",
      "第二种装扮\n",
      "西装\n",
      "领带\n",
      "皮鞋\n",
      "装扮的张贺\n"
     ]
    }
   ],
   "source": [
    "def main():\n",
    "    hezhang = Person(\"张贺\")\n",
    "    print(\"第一种装扮\")\n",
    "    hezhang.wear_T_shirts()\n",
    "    hezhang.wear_big_trouser()\n",
    "    hezhang.wear_sneakers()\n",
    "    hezhang.show()\n",
    "    \n",
    "    print(\"第二种装扮\")\n",
    "    hezhang.wear_suit()\n",
    "    hezhang.wear_tie()\n",
    "    hezhang.wear_leather_shoes()\n",
    "    hezhang.show()\n",
    "\n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "- 仅实现了基本功能\n",
    "- 如果添加“超人”装扮，需要如何做？需要在Person类中改代码；\n",
    "- 如果Person类除了穿衣服，还要支持吃饭，睡觉等功能，需要如何做？需要在Person类中改代码；\n",
    "- 综上，需要把“服饰”类和“人”类分离。\n",
    "\n",
    "## 改进版本1.0——人衣分离"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "class Person():\n",
    "    \n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "    \n",
    "    def show(self):\n",
    "        print(\"装扮的{}\".format(self.name))\n",
    "\n",
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class Finery(metaclass=ABCMeta):\n",
    "    \n",
    "    @abstractmethod\n",
    "    def show(self):\n",
    "        pass\n",
    "    \n",
    "class TShirts(Finery):\n",
    "    \n",
    "    def show(self):\n",
    "        print(\"T恤\")\n",
    "\n",
    "class BigTrouser(Finery):\n",
    "    \n",
    "    def show(self):\n",
    "        print(\"垮裤\")\n",
    "        \n",
    "class Sneakers(Finery):\n",
    "    \n",
    "    def show(self):\n",
    "        print(\"运动鞋\")\n",
    "        \n",
    "class Suit(Finery):\n",
    "    \n",
    "    def show(self):\n",
    "        print(\"西装\")\n",
    "        \n",
    "class Tie(Finery):\n",
    "    \n",
    "    def show(self):\n",
    "        print(\"领带\")\n",
    "        \n",
    "class LeatherShoes(Finery):\n",
    "    \n",
    "    def show(self):\n",
    "        print(\"皮鞋\")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "客户端代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "第一种装扮\n",
      "T恤\n",
      "垮裤\n",
      "运动鞋\n",
      "装扮的张贺\n",
      "第二种装扮\n",
      "西装\n",
      "领带\n",
      "皮鞋\n",
      "装扮的张贺\n"
     ]
    }
   ],
   "source": [
    "def main():\n",
    "    hezhang = Person(\"张贺\")\n",
    "    print(\"第一种装扮\")\n",
    "    t_shirts = TShirts()\n",
    "    big_trouser = BigTrouser()\n",
    "    sneakers = Sneakers()\n",
    "    \n",
    "    t_shirts.show()\n",
    "    big_trouser.show()\n",
    "    sneakers.show()\n",
    "    hezhang.show()\n",
    "    \n",
    "    print(\"第二种装扮\")\n",
    "    suit = Suit()\n",
    "    tie = Tie()\n",
    "    leather_shoes = LeatherShoes()\n",
    "    suit.show()\n",
    "    tie.show()\n",
    "    leather_shoes.show()\n",
    "    hezhang.show()\n",
    "\n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "分析以下代码:\n",
    "```\n",
    "    hezhang = Person(\"张贺\")\n",
    "    t_shirts = TShirts()\n",
    "    big_trouser = BigTrouser()\n",
    "    sneakers = Sneakers()\n",
    "    \n",
    "    t_shirts.show()\n",
    "    big_trouser.show()\n",
    "    sneakers.show()\n",
    "    hezhang.show()\n",
    "```\n",
    "用自然语言描述就是:\n",
    "- 先实例化出`hezhang`这个人类，准确的说，是没有穿衣服的人类; \n",
    "- 再实例化并穿上出各种衣服，T恤，垮裤，运动鞋等;\n",
    "- 再展示出来`hezhang`；\n",
    "\n",
    "但是服饰和人之间好像没有任何关系，那么如何用代码表示：\n",
    "\n",
    "- 实例化没穿衣服的人类\n",
    "- 为没穿衣服的人类穿上T恤\n",
    "- 为穿着T恤的人类穿上垮裤\n",
    "- 为穿着T恤,垮裤的人类穿上运动鞋\n",
    "\n",
    "这需要用到装饰模式。\n",
    "\n",
    "## 装饰模式\n",
    "\n",
    "装饰模式，为了动态的给一个对象添加一些额外的职责，就增加功能而言，装饰模式比生成子类更为灵活[DP]。\n",
    "\n",
    "装饰模式有以下几个主要组成部分:\n",
    "\n",
    "- 组件类Component：定义一个对象接口, 可以给这些对象动态的添加职责;\n",
    "- 具体组件类ConcretComponent：定义了一个具体对象，也可以给这个对象添加一些职责；\n",
    "- 装饰类Decorator：装饰抽象类，继承Component，从外类来扩展Component类的功能，对于Component而言，无需知道Decorator的存在；\n",
    "- 具体装饰类ConcretDecorator：具体装饰类，为Component添加职责\n",
    "\n",
    "用代码表示:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "from abc import ABCMeta, abstractmethod\n",
    "\n",
    "\n",
    "class Component(metaclass=ABCMeta):\n",
    "    \"\"\"\n",
    "    组件类Component:定义一个对象接口, 可以给这些对象动态的添加职责\n",
    "    \"\"\"\n",
    "    @abstractmethod\n",
    "    def operation(self):\n",
    "        pass\n",
    "    \n",
    "    \n",
    "class ConcreteComponent(Component):\n",
    "    \"\"\"\n",
    "    具体组件类ConcretComponent：定义了一个具体对象，也可以给这个对象添加一些职责\n",
    "    \"\"\"\n",
    "    def operation(self):\n",
    "        print(\"具体组件的操作\")\n",
    "      \n",
    "    \n",
    "class Decorator(Component):\n",
    "    \"\"\"\n",
    "    装饰类Decorator：装饰抽象类，继承Component，从外类来扩展Component类的功能，对于Component而言，无需知道Decorator的存在；\n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        self.component = None\n",
    "        \n",
    "    def set_component(self, component):\n",
    "        self.component = component\n",
    "    \n",
    "    def operation(self):\n",
    "        if self.component != None:\n",
    "            self.component.operation()\n",
    "            \n",
    "            \n",
    "class ConcreteDecratorA(Decorator):\n",
    "    \"\"\"\n",
    "    具体装饰类ConcretDecorator：具体装饰类，为Component添加职责\n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        self.added_operation = \"A:具体装饰类A独有操作\"\n",
    "        \n",
    "    def operation(self):\n",
    "        super().operation()\n",
    "        print(self.added_operation)\n",
    "        print(\"A:具体装饰对象A的操作\")\n",
    "        \n",
    "        \n",
    "class ConcreteDecratorB(Decorator):\n",
    "    \"\"\"\n",
    "    具体装饰类ConcretDecorator：具体装饰类，为Component添加职责\n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        self.added_operation = \"B:具体装饰类B独有操作\"\n",
    "        \n",
    "    def operation(self):\n",
    "        super().operation()\n",
    "        print(self.added_operation)\n",
    "        print(\"B:具体装饰对象B的操作\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "具体组件的操作\n",
      "A:具体装饰类A独有操作\n",
      "A:具体装饰对象A的操作\n",
      "B:具体装饰类B独有操作\n",
      "B:具体装饰对象B的操作\n"
     ]
    }
   ],
   "source": [
    "def main():\n",
    "    component = ConcreteComponent()\n",
    "    decorator_a = ConcreteDecratorA()\n",
    "    decorator_b = ConcreteDecratorB()\n",
    "    \n",
    "    decorator_a.set_component(component)\n",
    "    decorator_b.set_component(decorator_a)\n",
    "    decorator_b.operation()\n",
    "    \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 改进版本2.0——装饰模式"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [],
   "source": [
    "from abc import ABCMeta,abstractmethod\n",
    "\n",
    "\n",
    "class Person():\n",
    "    \"\"\"\n",
    "    人物类(组件类)\n",
    "    \"\"\"\n",
    "    def __init__(self, name):\n",
    "        self.name = name\n",
    "    \n",
    "    def show(self):\n",
    "        print(\"装扮的{}\".format(self.name))\n",
    "    \n",
    "    \n",
    "class Finery(Person):\n",
    "    \"\"\"\n",
    "    服饰类(装饰类)\n",
    "    \"\"\"\n",
    "    def __init__(self):\n",
    "        self.component = None\n",
    "    \n",
    "    def decorate(self, component):\n",
    "        self.component = component\n",
    "        \n",
    "    \n",
    "    def show(self):\n",
    "        if self.component != None:\n",
    "            self.component.show()\n",
    "            \n",
    "\n",
    "class TShirts(Finery):\n",
    "    \"\"\"\n",
    "    具体装饰类\n",
    "    \"\"\"\n",
    "    def show(self):\n",
    "        print(\"T恤\")\n",
    "        super().show()\n",
    "\n",
    "class BigTrouser(Finery):\n",
    "    \"\"\"\n",
    "    具体装饰类\n",
    "    \"\"\"\n",
    "    def show(self):\n",
    "        print(\"垮裤\")\n",
    "        super().show()\n",
    "        \n",
    "        \n",
    "class Sneakers(Finery):\n",
    "    \"\"\"\n",
    "    具体装饰类\n",
    "    \"\"\"\n",
    "    def show(self):\n",
    "        print(\"运动鞋\")\n",
    "        super().show()\n",
    "        \n",
    "class Suit(Finery):\n",
    "    \"\"\"\n",
    "    具体装饰类\n",
    "    \"\"\"\n",
    "    def show(self):\n",
    "        print(\"西装\")\n",
    "        super().show()\n",
    "        \n",
    "class Tie(Finery):\n",
    "    \"\"\"\n",
    "    具体装饰类\n",
    "    \"\"\"\n",
    "    def show(self):\n",
    "        print(\"领带\")\n",
    "        super().show()\n",
    "        \n",
    "class LeatherShoes(Finery):\n",
    "    \"\"\"\n",
    "    具体装饰类\n",
    "    \"\"\"\n",
    "    def show(self):\n",
    "        print(\"皮鞋\")\n",
    "        super().show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "客户端代码"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "运动鞋\n",
      "垮裤\n",
      "T恤\n",
      "装扮的张贺\n"
     ]
    }
   ],
   "source": [
    "def main():\n",
    "    hezhang = Person(\"张贺\")\n",
    "    t_shirts = TShirts()\n",
    "    big_trouser = BigTrouser()\n",
    "    sneakers = Sneakers()\n",
    "    \n",
    "    t_shirts.decorate(hezhang)\n",
    "    big_trouser.decorate(t_shirts)\n",
    "    sneakers.decorate(big_trouser)\n",
    "    sneakers.show()\n",
    "    \n",
    "main()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### 点评\n",
    "\n",
    "上述客户端代码可以用自然语言描述为：\n",
    "\n",
    "- 实例化没穿衣服的人类\n",
    "- 为没穿衣服的人类穿上T恤\n",
    "- 为穿着T恤的人类穿上垮裤\n",
    "- 为穿着T恤,垮裤的人类穿上运动鞋\n",
    "\n",
    "所以，装饰模型是为已有功能动态的添加更多功能的一种方式，它把每个要装饰的功能放在单独的类中，并让这个类包装它所要装饰的对象，因此，当需要执行特殊行为时，客户端代码可以在运行时根据需要有选择的，按顺序的使用装饰功能包装对象。\n",
    "\n",
    "装饰模式的优点在于，把类中的装饰功能从类中搬移出去，这样可以简化原有的类，有效的把类的`核心职责`和`装饰功能`区分开了。而且可以去除相关类中重复的装饰逻辑，即一个装饰功能可以给多个不同的类使用。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
